package com.gitee.wsl.android.ui.spannable.span

import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Paint.FontMetricsInt
import android.os.Build
import android.text.style.ReplacementSpan
import android.view.View
import android.view.View.OnAttachStateChangeListener
import android.view.ViewGroup
import android.widget.FrameLayout
import android.widget.TextView
import timber.log.Timber


class InternalViewSpan(private var view: View?=null,
    private var mTextView: TextView?=null
) : ReplacementSpan() {

    private lateinit var  mLayout: InternalLayout
    /**
     * 设置对齐类型
     *
     */
     var mAlignType = AlignType.bottom
         set(value) {
             if (field != value) {
                 field = value
                 update()
             }
         }
    /**
     * 设置高度类型
     *
     */
     var mHeightType = HeightType.match
         set(value) {
             if (field != value) {
                 field = value
                 update()
             }
         }
    private var mIsPrepared = false
    private var mIsDirty = false

    /**
     * 更新span
     */
    fun update() {
        if (mIsPrepared) {
            Timber.d("update " + this@InternalViewSpan)
            mTextView?.removeCallbacks(mUpdateRunnable)
            mTextView?.post(mUpdateRunnable)
        }
    }

    private val mUpdateRunnable = Runnable {
        if (mIsPrepared) {
            val text = mTextView?.text
            mTextView?.text = text
        }
    }

    override fun draw(
        canvas: Canvas,
        text: CharSequence,
        start: Int,
        end: Int,
        x: Float,
        top: Int,
        y: Int,
        bottom: Int,
        paint: Paint
    ) {
        Timber.d( "draw " + this@InternalViewSpan)
        canvas.save()
        var transY = top
        if (mAlignType == AlignType.baseline) {
            val descent = Math.abs(paint.fontMetricsInt.descent)
            if (mHeightType == HeightType.match) {
                transY -= descent
            } else if (mHeightType == HeightType.ascent) {
                val height = Math.abs(paint.fontMetricsInt.bottom - paint.fontMetricsInt.top)
                val ascent = Math.abs(paint.fontMetricsInt.ascent)
                val delta = height - descent - ascent
                transY += delta
            }
        } else if (mAlignType == AlignType.bottom) {
            if (mHeightType == HeightType.match) {
            } else if (mHeightType == HeightType.ascent) {
                val height = Math.abs(paint.fontMetricsInt.bottom - paint.fontMetricsInt.top)
                val ascent = Math.abs(paint.fontMetricsInt.ascent)
                val delta = height - ascent
                transY += delta
            }
        }
        canvas.translate(x, transY.toFloat())
        mLayout.draw(canvas)
        canvas.restore()
    }

    override fun getSize(
        paint: Paint,
        text: CharSequence,
        start: Int,
        end: Int,
        fm: FontMetricsInt?
    ): Int {
        val height = getLineHeight(fm)
        if (mIsDirty) {
            measureLayout(height)
            Timber.d("getSize width:" + mLayout.measuredWidth + " height:" + height + " " + this@InternalViewSpan)
        }
        return mLayout.measuredWidth
    }

    private fun getLineHeight(fm: FontMetricsInt?): Int {
        var height = 0
        val fmTextView = mTextView?.paint?.fontMetricsInt
        if (fmTextView != null) height = getFontMetricsIntHeight(fmTextView)
        if (height <= 0) {
            if (fm != null) height = getFontMetricsIntHeight(fm)
        }
        val measuredHeight = mLayout.measuredHeight
        if (height != measuredHeight) setDirty(true)
        return height
    }

    private fun getFontMetricsIntHeight(fm: FontMetricsInt): Int {
        var height = 0
        if (mHeightType == HeightType.match) {
            height = Math.abs(fm.bottom - fm.top)
        } else if (mHeightType == HeightType.ascent) {
            height = Math.abs(fm.ascent)
        }
        return height
    }

    private fun setDirty(dirty: Boolean): Boolean {
        if (mIsDirty != dirty) {
            mIsDirty = dirty
            return true
        }
        return false
    }

    private fun measureLayout(height: Int) {
        val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
        val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY)
        mLayout.measure(widthMeasureSpec, heightMeasureSpec)
        mLayout.layout(0, 0, mLayout.measuredWidth, mLayout.measuredHeight)
        setDirty(false)
    }

    private inner class InternalLayout(context: Context) : FrameLayout(context) {
        override fun requestLayout() {
            super.requestLayout()
            if (setDirty(true)) {
                Timber.d("requestLayout " + this@InternalViewSpan)
                update()
            }
        }

        override fun onDraw(canvas: Canvas) {
            super.onDraw(canvas)
            setPrepared(true)
        }
    }

    private fun setPrepared(prepared: Boolean) {
        if (mIsPrepared == prepared) return
        if (prepared && !isViewAttached(mTextView!!)) return
        mIsPrepared = prepared
        Timber.d( "setPrepared:" + prepared + " " + this@InternalViewSpan)
        if (prepared) {
            mTextView?.addOnAttachStateChangeListener(mOnAttachStateChangeListener)
            onPrepared()
        } else {
            mTextView?.removeOnAttachStateChangeListener(mOnAttachStateChangeListener)
            onDestroy()
        }
    }

    private val mOnAttachStateChangeListener: OnAttachStateChangeListener =
        object : OnAttachStateChangeListener {
            override fun onViewAttachedToWindow(v: View) {}
            override fun onViewDetachedFromWindow(v: View) {
                setPrepared(false)
            }
        }

   fun init(view: View,textView: TextView) {
        this.view=view
        this.mTextView=textView
        removeViewFromParent(view)
        mLayout = InternalLayout(view.context)
        mLayout.addView(view)
    }

    protected fun onPrepared() {}
    protected fun onDestroy() {}
    enum class AlignType {
        bottom, baseline
    }

    enum class HeightType {
        match, ascent
    }

    companion object {
        private fun removeViewFromParent(view: View?) {
            if (view == null) return
            val parent = view.parent ?: return
            try {
                val viewGroup = parent as ViewGroup
                viewGroup.removeView(view)
            } catch (e: Exception) {
                Timber.e(e)
            }
        }

        private fun isViewAttached(view: View): Boolean {
            return if (Build.VERSION.SDK_INT >= 19) view.isAttachedToWindow else view.windowToken != null
        }
    }
}